#include "VBOModel.h"

#include "glmh.h"

#include "ImageLoader.h"

VBOModel::VBOModel(string modelFileName) : textureId(-1)
{
	glmModel = glmReadOBJ((char*) modelFileName.c_str());

	scale = glmUnitize(glmModel);

	glmhCorrect(glmModel);
	//glmFacetNormals(model);
	//glmVertexNormals(model, smoothing_angle);

	makeVBO(glmModel);
}

VBOModel::~VBOModel(void)
{
	free(IndexVBOIDs);
}

void VBOModel::draw()
{
	glPushMatrix();

	glTranslatef(glmModel->position[0], glmModel->position[1], glmModel->position[2]);

	glBindBuffer(GL_ARRAY_BUFFER, VertexVBOID);
	glEnableClientState(GL_VERTEX_ARRAY);
	glVertexPointer(3, GL_FLOAT, sizeof(MyVertex), BUFFER_OFFSET(0));

	glEnableClientState(GL_NORMAL_ARRAY);
	glNormalPointer(GL_FLOAT, sizeof(MyVertex), BUFFER_OFFSET(12));   

	glClientActiveTexture(GL_TEXTURE0);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glTexCoordPointer(2, GL_FLOAT, sizeof(MyVertex), BUFFER_OFFSET(24));   

	int groupIndex = 0;
	for (GLMgroup* group = glmModel->groups; group; group = group->next)
	{
		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, glmModel->materials[group->material].ambient);
		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, glmModel->materials[group->material].diffuse);
		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, glmModel->materials[group->material].specular);
		glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, glmModel->materials[group->material].shininess);
		
		if (glmModel->materials[group->material].texturename != NULL)
		{
			if (textureId == -1)
			{
				Image *image = loadBMP(glmModel->materials[1].texturename);

				glGenTextures(1, &textureId);
				glBindTexture(GL_TEXTURE_2D, textureId);
				glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image->width, image->height, 0, GL_RGB, GL_UNSIGNED_BYTE, image->pixels);

				delete image;
			}	

			glEnable(GL_TEXTURE_2D);
			glBindTexture(GL_TEXTURE_2D, textureId);

			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		}
		
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexVBOIDs[groupIndex]);
		glDrawElements(GL_TRIANGLES, group->numtriangles * 3, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0)); 

		groupIndex++;
	}

	glPopMatrix();
}

GLfloat VBOModel::getScale()
{
	return scale;
}


void VBOModel::makeVBO(GLMmodel* glmModel)
{
	//
	// make vertex VBO

	MyVertex* pvertex = (MyVertex*) malloc(sizeof(MyVertex) * glmModel->numtriangles * 3);

	for (unsigned int i = 0; i < glmModel->numtriangles; i++) {

		GLMtriangle glm_triangle = glmModel->triangles[i];

		for (int j = 0; j < 3; j++) {

			GLfloat* vertices = &glmModel->vertices[3 * glm_triangle.vindices[j]];
			GLfloat* normals = &glmModel->normals[3 * glm_triangle.nindices[j]];
			GLfloat* texcoords = &glmModel->texcoords[2 * glm_triangle.tindices[j]];

			pvertex[3*i + j].x = vertices[0];
			pvertex[3*i + j].y = vertices[1];
			pvertex[3*i + j].z = vertices[2];

			pvertex[3*i + j].nx = normals[0];
			pvertex[3*i + j].ny = normals[1];
			pvertex[3*i + j].nz = normals[2];

			if (glmModel->numtexcoords)
			{
				pvertex[3*i + j].s0 = texcoords[0];
				pvertex[3*i + j].t0 = texcoords[1];
			}
			else
			{
				pvertex[3*i + j].s0 = 0;
				pvertex[3*i + j].t0 = 0;
			}
		}
	}

	glGenBuffers(1, &VertexVBOID);
	glBindBuffer(GL_ARRAY_BUFFER, VertexVBOID);
	glBufferData(GL_ARRAY_BUFFER, sizeof(MyVertex) * glmModel->numtriangles * 3, &pvertex[0].x, GL_STATIC_DRAW);

	free(pvertex);

	//
	// make face indices VBO by group

	IndexVBOIDs = (GLuint*) malloc(sizeof(GLuint) * glmModel->numgroups);

	glGenBuffers(glmModel->numgroups, IndexVBOIDs);

	int groupIndex = 0;
	for (GLMgroup* group = glmModel->groups; group; group = group->next)
	{
		GLushort* pindices = (GLushort*) malloc(sizeof(GLushort) * group->numtriangles * 3);

		for (unsigned int i = 0; i < group->numtriangles; i++)
		{
			for (int j = 0; j < 3; j++)
			{
				pindices[3*i + j] = 3 * group->triangles[i] + j;
			}
		}
		
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexVBOIDs[groupIndex]);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * group->numtriangles * 3, pindices, GL_STATIC_DRAW);
	
		free(pindices);

		groupIndex++;
	}
	
}